home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / 8250.c next >
Encoding:
C/C++ Source or Header  |  1994-08-10  |  13.5 KB  |  589 lines

  1. /* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC
  2.  *
  3.  * 16550A support plus some statistics added mah@hpuviea.at 15/7/89
  4.  *
  5.  * CTS hardware flow control from dkstevens@ucdavis,
  6.  * additional stats from delaroca@oac.ucla.edu added by karn 4/17/90
  7.  * 8250 support for above by PA0GRI (hardware kick, interrupt does not come up)
  8.  */
  9. #include <stdio.h>
  10. #include <dos.h>
  11. #include "global.h"
  12. #include "config.h"
  13. #ifdef ASY
  14. #include "mbuf.h"
  15. #include "proc.h"
  16. #include "iface.h"
  17. #include "asy.h"
  18. #include "pc.h"
  19. #include "slip.h"
  20. #include "8250.h"
  21. #ifdef NRS
  22. #include "nrs.h"
  23. #endif
  24.  
  25. static int near asyrxint __ARGS((struct asy *asyp));
  26. static void near asytxint __ARGS((int dev));
  27. static void near asymsint __ARGS((int dev));
  28.  
  29. struct asy Asy[ASY_MAX];
  30. /* ASY interrupt handlers */
  31. static INTERRUPT (*Handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
  32.  
  33. /* Initialize asynch port "dev" */
  34. int
  35. asy_init(dev,iface,arg1,arg2,bufsize,trigchar,cts)
  36. int dev;
  37. struct iface *iface;
  38. char *arg1,*arg2;    /* Attach args for address and vector */
  39. unsigned bufsize;
  40. int trigchar;
  41. char cts;
  42. {
  43.     struct fifo *fp;
  44.     int i_state;
  45.     struct asy *ap = &Asy[dev];
  46.     unsigned base;
  47.  
  48.     if(bufsize) {
  49.         ap->iface = iface;
  50.         ap->addr = htoi(arg1);
  51.         ap->vec = htoi(arg2);
  52.         ap->cts_flow_control = cts;
  53.         ap->trigchar = trigchar;
  54.  
  55.         /* Set up receiver FIFO */
  56.         fp = &ap->fifo;
  57.         fp->buf = mxallocw(bufsize);
  58.         fp->bufsize = bufsize;
  59.         fp->wp = fp->rp = fp->buf;
  60.         fp->cnt = fp->hiwat = fp->overrun = 0;
  61.     }
  62.  
  63.     base = ap->addr;
  64.     /* Purge the receive data buffer */
  65.     (void)inportb(base+RBR);
  66.  
  67.     i_state = dirps();
  68.  
  69.     /* Save original interrupt vector, mask state, control bits */
  70.     ap->save.vec = getirq(ap->vec);
  71.     ap->save.mask = getmask(ap->vec);
  72.     ap->save.lcr = inportb(base+LCR);
  73.     ap->save.ier = inportb(base+IER);
  74.     ap->save.mcr = inportb(base+MCR);
  75.  
  76.     /* save speed bytes */
  77.     setbit(base+LCR,LCR_DLAB);
  78.     ap->save.divl = inportb(base+DLL);
  79.     ap->save.divh = inportb(base+DLM);
  80.     clrbit(base+LCR,LCR_DLAB);
  81.  
  82.     /* Set interrupt vector to SIO handler */
  83.     setirq(ap->vec,Handle[dev]);
  84.  
  85.     /* Set line control register: 8 bits, no parity */
  86.     outportb(base+LCR,(char)LCR_8BITS);
  87.  
  88.     /* determine if 16550A, turn on FIFO mode and clear RX and TX FIFOs */
  89.     outportb(base+FCR,(char) FIFO_ENABLE);
  90.  
  91.     /* According to National ap note AN-493, the FIFO in the 16550 chip
  92.      * is broken and must not be used. To determine if this is a 16550A
  93.      * (which has a good FIFO implementation) check that both bits 7
  94.      * and 6 of the IIR are 1 after setting the fifo enable bit. If
  95.      * not, don't try to use the FIFO.
  96.      */
  97.     if ((inportb(base+IIR) & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) {
  98.         ap->is_16550a = 1;
  99.         outportb(base+FCR,(char) FIFO_SETUP);
  100.     } else {
  101.         /* Chip is not a 16550A. In case it's a 16550 (which has a
  102.          * broken FIFO), turn off the FIFO bit.
  103.          */
  104.         outportb(base+FCR,(char)0);
  105.         ap->is_16550a = 0;
  106.      }
  107.     /* Turn on receive interrupt enable in 8250, leave transmit
  108.      * and modem status interrupts turned off for now
  109.      */
  110.     outportb(base+IER,(char)IER_DAV);
  111.  
  112.     /* Set modem control register: assert DTR, RTS, turn on 8250
  113.      * master interrupt enable (connected to OUT2)
  114.      */
  115.     outportb(base+MCR,(char)(MCR_DTR|MCR_RTS|MCR_OUT2));
  116.  
  117.     /* Enable interrupt */
  118.     maskon(ap->vec);
  119.     restore(i_state);
  120.     return 0;
  121. }
  122.  
  123. int
  124. asy_stop(iface,tmp)
  125. struct iface *iface;
  126. int tmp;
  127. {
  128.     int i_state;
  129.     struct asy *ap = &Asy[iface->dev];
  130.     unsigned base;
  131.  
  132.     /*-------------------------------------------------------------------*
  133.     * A few notes to the delay below:                                    *
  134.     * It sometimes happened, that NOS simply hangs when an exit is issued*
  135.     * and more than one remote session is active.                        *
  136.     * the reset_all() in between doexit() resets all the remote connec-  *
  137.     * tions causing a RST packet being sent to all those remote instances*
  138.     * During this work, NOS tries to detach the interfaces and in case   *
  139.     * a 16650A is involved, the hardware FIFO is reset and POOF!         *
  140.     * The delay gives enough time to send the packets (I hope) DK5DC     *
  141.     *--------------------------------------------------------------------*/
  142. #ifdef MSDOS
  143.     pause(1000);                         /* let the chip send all data  */
  144. #endif
  145.  
  146.     if(!tmp) {
  147.         ap->iface = NULLIF;
  148.         /* Release slip or nrs block */
  149. #ifdef  SLIP
  150.         if(Slip[iface->xdev].iface == iface)
  151.             Slip[iface->xdev].iface = NULLIF;
  152. #endif
  153. #ifdef  NRS
  154.         if(Nrs[iface->xdev].iface == iface)
  155.             Nrs[iface->xdev].iface = NULLIF;
  156. #endif
  157.     }
  158.  
  159.     base = ap->addr;
  160.     /* Purge the receive data buffer */
  161.     (void)inportb(base+RBR);
  162.  
  163.     /* and hardware fifos if available */
  164.     if (ap->is_16550a)
  165.         outportb(base+FCR,(char) FIFO_SETUP);
  166.  
  167.     /* Restore original interrupt vector and 8259 mask state */
  168.     i_state = dirps();
  169.     setirq(ap->vec,ap->save.vec);
  170.     if (ap->save.mask)
  171.         maskon(ap->vec);
  172.     else
  173.         maskoff(ap->vec);
  174.  
  175.     /* Restore speed regs */
  176.     setbit(base+LCR,LCR_DLAB);
  177.     outportb(base+DLL,ap->save.divl);    /* Low byte */
  178.     outportb(base+DLM,ap->save.divh);    /* Hi byte */
  179.     clrbit(base+LCR,LCR_DLAB);
  180.  
  181.     /* Restore control regs */
  182.     outportb(base+LCR,ap->save.lcr);
  183.     outportb(base+IER,ap->save.ier);
  184.     outportb(base+MCR,ap->save.mcr);
  185.     restore(i_state);
  186.     if(!tmp)
  187.         xfree(ap->fifo.buf);
  188.     return 0;
  189. }
  190.  
  191. /* Asynchronous line I/O control */
  192. int
  193. asy_ioctl(iface,argc,argv)
  194. struct iface *iface;
  195. int argc;
  196. char *argv[];
  197. {
  198.     if(argc < 1){
  199.         tprintf("%u\n",Asy[iface->dev].speed);
  200.         return 0;
  201.     }
  202.     return asy_speed(iface->dev,atoi(argv[0]));
  203. }
  204.  
  205. /* Set asynch line speed */
  206. int
  207. asy_speed(dev,speed)
  208. int dev;
  209. long speed;
  210. {
  211.     int i_state;
  212.     unsigned base = Asy[dev].addr;
  213.     int32 divisor = BAUDCLK / speed;
  214.  
  215.     if(speed == 0 || dev >= ASY_MAX)
  216.         return -1;
  217.  
  218.     Asy[dev].speed = speed;
  219.  
  220.     i_state = dirps();
  221.  
  222.     /* Purge the receive data buffer */
  223.     (void)inportb(base+RBR);
  224.     if (Asy[dev].is_16550a)       /* clear tx+rx fifos */
  225.         outportb(base+FCR,(char) FIFO_SETUP);
  226.  
  227.     /* Turn on divisor latch access bit */
  228.     setbit(base+LCR,LCR_DLAB);
  229.  
  230.     /* Load the two bytes of the register */
  231.     outportb(base+DLL,(char)(divisor & 0xff));        /* Low byte */
  232.     outportb(base+DLM,(char)((divisor >> 8) & 0xff));    /* Hi byte */
  233.  
  234.     /* Turn off divisor latch access bit */
  235.     clrbit(base+LCR,LCR_DLAB);
  236.  
  237.     restore(i_state);
  238.     return 0;
  239. }
  240.  
  241. /* Start transmission of a buffer on the serial transmitter */
  242. static void near
  243. asy_output(int dev,char *buf,unsigned short cnt)
  244. {
  245.     int i_state, ier;
  246.     struct asy *asyp = &Asy[dev];
  247.     unsigned base = asyp->addr;
  248.     struct dma *dp = &asyp->dma;
  249.  
  250.     if(dev < 0 || dev >= ASY_MAX || asyp->iface == NULLIF)
  251.         return;
  252.  
  253.     i_state = dirps();
  254.  
  255.     if(dp->flags){
  256.         restore(i_state);
  257.         return;    /* Already busy */
  258.     }
  259.     dp->data = buf;
  260.     dp->cnt = cnt;
  261.     dp->flags = 1;
  262.  
  263.     if(asyp->cts_flow_control){
  264.         /* CTS flow control is enabled; let the modem control
  265.          * interrupt enable transmit interrupts if CTS is off
  266.          */
  267.         ier = IER_MS;
  268.         if(inportb(base+MSR) & MSR_CTS)
  269.             ier |= IER_TxE;
  270.     } else {
  271.         /* Enable transmit interrupts; this will cause an immediate
  272.          * interrupt that will start things going
  273.          */
  274.         ier = IER_TxE;
  275.     }
  276.     setbit(base+IER,ier);
  277.     /* "Kick start" the transmitter interrupt routine, in case just
  278.      * setting the interrupt enable bit doesn't case an interrupt
  279.      */
  280.     if(ier & IER_TxE)
  281.         asytxint(dev);
  282.     restore(i_state);
  283. }
  284.  
  285. /* Blocking read from asynch line
  286.  * Returns count of characters read
  287.  */
  288. int
  289. get_asy(dev)
  290. int dev;
  291. {
  292.     struct fifo *fp = &Asy[dev].fifo;
  293.     int c, i_state = dirps();
  294.  
  295.     while(fp->cnt == 0){
  296.         if(pwait(fp) != 0){
  297.             restore(i_state);
  298.             return -1;
  299.         }
  300.     }
  301.     fp->cnt--;
  302.     restore(i_state);
  303.  
  304.     c = *fp->rp++;
  305.  
  306.     if(fp->rp >= &fp->buf[fp->bufsize])
  307.         fp->rp = fp->buf;
  308.  
  309.     return c;
  310. }
  311.  
  312. /* Interrupt handler for 8250 asynch chip */
  313. void
  314. asyint(dev)
  315. int dev;
  316. {
  317.     char iir;
  318.     struct asy *asyp = &Asy[dev];
  319.     unsigned base = asyp->addr;
  320.  
  321.     while(((iir = inportb(base+IIR)) & IIR_IP) == 0){
  322.         switch(iir & IIR_ID){
  323.         case IIR_RDA:    /* Receiver interrupt */
  324.             asyrxint(asyp);
  325.             break;
  326.         case IIR_THRE:    /* Transmit interrupt */
  327.             asytxint(dev);
  328.             break;
  329.         case IIR_MSTAT:    /* Modem status change */
  330.             asymsint(dev);
  331.             asyp->cts_flow_ints++;
  332.             break;
  333.         }
  334.         /* should happen at end of a single slip packet */
  335.         if(iir & IIR_FIFO_TIMEOUT)
  336.             asyp->fifotimeouts++;
  337.     }
  338. }
  339.  
  340. /* Process 8250 receiver interrupts */
  341. static int near
  342. asyrxint(asyp)
  343. struct asy *asyp;
  344. {
  345.     int c, lsr, cnt = 0, trigseen = 0;
  346.  
  347.     unsigned base = asyp->addr;
  348.     struct fifo *fp = &asyp->fifo;
  349.  
  350.     asyp->rxints++;
  351.  
  352.     for(;;){
  353.         if((lsr = inportb(base+LSR)) & LSR_OE)
  354.             asyp->overrun++;
  355.  
  356.         if(lsr & LSR_DR){
  357.             asyp->rxchar++;
  358.  
  359.             if((c = inportb(base+RBR)) == asyp->trigchar)
  360.                 trigseen = 1;
  361.  
  362.             /* If buffer is full, we have no choice but
  363.              * to drop the character
  364.              */
  365.             if(fp->cnt != fp->bufsize){
  366.                 *fp->wp++ = c;
  367.                 if(fp->wp >= &fp->buf[fp->bufsize])
  368.                     /* Wrap around */
  369.                     fp->wp = fp->buf;
  370.  
  371.                 if(++fp->cnt > fp->hiwat)
  372.                     fp->hiwat = fp->cnt;
  373.  
  374.                 cnt++;
  375.             } else
  376.                 fp->overrun++;
  377.         } else
  378.             break;
  379.     }
  380.     if(cnt > asyp->rxhiwat)
  381.         asyp->rxhiwat = cnt;
  382.     if(trigseen)
  383.         psignal(fp,1);
  384.     return cnt;
  385. }
  386.  
  387. /* Handle 8250 transmitter interrupts */
  388. static void near
  389. asytxint(dev)
  390. int dev;
  391. {
  392.     int count;
  393.     struct asy *asyp = &Asy[dev];
  394.     unsigned base = asyp->addr;
  395.     struct dma *dp = &asyp->dma;
  396.  
  397.     asyp->txints++;
  398.  
  399.     if(!dp->flags){
  400.         /* "Shouldn't happen", but disable transmit
  401.          * interrupts anyway
  402.          */
  403.         clrbit(base+IER,IER_TxE);
  404.         return;    /* Nothing to send */
  405.     }
  406.     if(!(inportb(base+LSR) & LSR_THRE))
  407.         return;    /* Not really ready */
  408.  
  409.     /* If it's a 16550A, load up to 16 chars into the tx hw fifo
  410.      * at once. With an 8250, it can be one char at most.
  411.      */
  412.     if(asyp->is_16550a){
  413.         count = (int)min(dp->cnt,OUTPUT_FIFO_SIZE);
  414.  
  415.         /* 16550A: LSR_THRE will drop after the first char loaded
  416.          * so we can't look at this bit to determine if the hw fifo is
  417.          * full. There seems to be no way to determine if the tx fifo
  418.          * is full (any clues?). So we should never get here while the
  419.          * fifo isn't empty yet.
  420.          */
  421.         asyp->txchar += count;
  422.         dp->cnt -= count;
  423. #ifdef    XXX        /* This is apparently too fast for some chips */
  424.         dp->data = outbuf(base+THR,dp->data,count);
  425. #else
  426.         while(count-- != 0)
  427.             outportb(base+THR,*dp->data++);
  428. #endif
  429.         if(dp->cnt == 0){
  430.             dp->flags = 0;
  431.             /* Disable transmit interrupts */
  432.             clrbit(base+IER,IER_TxE);
  433.             psignal(asyp,1);
  434.         }
  435.     } else {    /* 8250 */
  436.         do {
  437.             asyp->txchar++;
  438.             outportb(base+THR,*dp->data++);
  439.  
  440.             if(--dp->cnt == 0){
  441.                 dp->flags = 0;
  442.                 /* Disable transmit interrupts */
  443.                 clrbit(base+IER,IER_TxE);
  444.                 psignal(asyp,1);
  445.                 break;
  446.             }
  447.         } while(inportb(base+LSR) & LSR_THRE);
  448.     }
  449. }
  450.  
  451. /* Handle 8250 modem status change */
  452. static void near
  453. asymsint(dev)
  454. int dev;
  455. {
  456.     struct asy *asyp = &Asy[dev];
  457.     unsigned base = asyp->addr;
  458.     struct dma *dp = &asyp->dma;
  459.     char msr = inportb(base+MSR);
  460.  
  461.     if (asyp->cts_flow_control) {
  462.         if(msr & MSR_CTS){
  463.             /* CTS now asserted, enable Transmit interrupts */
  464.             if(dp->flags) {
  465.                 setbit(base+IER,IER_TxE);
  466. /*                asytxint(dev); */
  467.             }
  468.         } else {
  469.             /* CTS now dropped, disable Transmit interrupts */
  470.             clrbit(base+IER,IER_TxE);
  471.         }
  472.     }
  473. }
  474.  
  475. /* Poll the asynch input queues; called on every clock tick.
  476.  * This helps limit the interrupt ring buffer occupancy when long
  477.  * packets are being received.
  478.  */
  479. void
  480. asytimer()
  481. {
  482.     int i;
  483.     struct asy *asyp;
  484.     struct fifo *fp;
  485.  
  486.     for(i = 0; i < ASY_MAX; i++) {
  487.         asyp = &Asy[i];
  488.         fp = &asyp->fifo;
  489.  
  490.         if(fp->cnt != 0)
  491.             psignal(fp,1);
  492.         if(asyp->dma.flags != 0 && (inportb(asyp->addr+LSR) & LSR_THRE) ) {
  493.             asyp->txto++;
  494.             asytxint(asyp->iface->dev);
  495.         }
  496.     }
  497. }
  498.  
  499. int
  500. doasystat(argc,argv,p)
  501. int argc;
  502. char *argv[];
  503. void *p;
  504. {
  505.     struct asy *asyp;
  506.  
  507.     for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
  508.         if(asyp->iface == NULLIF)
  509.             continue;
  510.  
  511.         tprintf("%s:",asyp->iface->name);
  512.         if(asyp->is_16550a)
  513.             tputs(" [NS16550A]");
  514.         if(asyp->trigchar != -1)
  515.             tprintf(" [trigger 0x%02x]",asyp->trigchar);
  516.         if(asyp->cts_flow_control)
  517.             tputs(" [cts flow control]");
  518.         tprintf(" %u bps\n",asyp->speed);
  519.         tprintf(" RX: int %lu chr %lu hwovrn %lu hwhiwat %lu",
  520.             asyp->rxints,
  521.             asyp->rxchar,
  522.             asyp->overrun,
  523.             asyp->rxhiwat);
  524.         asyp->rxhiwat = 0;
  525.         if(asyp->is_16550a)
  526.             tprintf(" fifoTO %lu",asyp->fifotimeouts);
  527.         tprintf(" swovrn %lu swhiwat %u\n",
  528.             asyp->fifo.overrun,
  529.             asyp->fifo.hiwat);
  530.         asyp->fifo.hiwat = 0;
  531.         tprintf(" TX: int %lu chr %lu sndq %u CTS/RLSD %lu int %lu\n",
  532.             asyp->txints,
  533.             asyp->txchar,
  534.             len_q(asyp->sndq),
  535.             asyp->cts_flow_ints,
  536.             asyp->txto);
  537.     }
  538.     return 0;
  539. }
  540.  
  541. /* Send a message on the specified serial line */
  542. int
  543. asy_send(dev,bp)
  544. int dev;
  545. struct mbuf *bp;
  546. {
  547.     if(dev < 0 || dev >= ASY_MAX)
  548.         return -1;
  549.  
  550.     enqueue(&Asy[dev].sndq,bp);
  551.     return 0;
  552. }
  553.  
  554. /* Serial transmit process, common to all protocols */
  555. void
  556. asy_tx(dev,p1,p2)
  557. int dev;
  558. void *p1;
  559. void *p2;
  560. {
  561.     struct mbuf *bp;
  562.     int i_state;
  563.     struct asy *asyp = &Asy[dev];
  564.     struct dma *dp = &asyp->dma;
  565.  
  566.     for(;;){
  567.         /* Fetch a buffer for transmission */
  568.         while(asyp->sndq == NULLBUF)
  569.             pwait(&asyp->sndq);
  570.         bp = dequeue(&asyp->sndq);
  571.  
  572.         while(bp != NULLBUF){
  573.             /* Start the transmitter */
  574.             asy_output(dev,bp->data,bp->cnt);
  575.  
  576.             /* Wait for completion */
  577.             i_state = dirps();
  578.             while(dp->flags == 1)
  579.                 pwait(asyp);
  580.             restore(i_state);
  581.  
  582.             /* Now do next buffer on chain */
  583.             bp = free_mbuf(bp);
  584.         }
  585.     }
  586. }
  587.  
  588. #endif /* ASY */
  589.